package com.qianyitian.hope2.analyzer.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.io.Files;
import com.qianyitian.hope2.analyzer.analyzer.DemarkAnalyzer;
import com.qianyitian.hope2.analyzer.config.Constant;
import com.qianyitian.hope2.analyzer.config.PropertyConfig;
import com.qianyitian.hope2.analyzer.funds.model.*;
import com.qianyitian.hope2.analyzer.model.AnalyzeResult;
import com.qianyitian.hope2.analyzer.model.DDU;
import com.qianyitian.hope2.analyzer.model.Stock;
import com.qianyitian.hope2.analyzer.model.SymbolList;
import com.qianyitian.hope2.analyzer.util.AlgorithmUtil;
import com.qianyitian.hope2.analyzer.util.Utils;
import com.qianyitian.hope2.sdk.funds.FundBar;
import com.qianyitian.hope2.sdk.funds.FundInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static j2html.TagCreator.*;
import static j2html.TagCreator.td;

@Service
public class FundReportService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    public static int ONE_YEAR_DAYS = 243;

    String first_pre_previous_report_date = "20210331";
    String pre_previous_report_date = "2021-06-30";
    String previous_report_date = "2021-09-30";
    String new_report_date = "2021-12-31";

    @Resource(name = "redisStorageService")
    private IReportStorageService reportService;
    @Autowired
    private StockService favoriteStockService;
    @Autowired
    PropertyConfig propertyConfig;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    DemarkService demarkService;

    @GetMapping("/fund/maxBackdraw/{code}")
    @CrossOrigin
    public String maxBackdraw(@PathVariable String code, @RequestParam(value = "days", required = false, defaultValue = "200") Integer days) {
        String content = favoriteStockService.getFundDetail(code);
        if (content == null) {
            return null;
        }
        FundInfo fundDetail = JSON.parseObject(content, FundInfo.class);

        FundBar[] fundBars = AlgorithmUtil.maxDrawback(fundDetail.getFundBarList(), days);

        long startPoint = Constant.ONE_DAY_MILLISECONDS * fundBars[0].getDate().toEpochDay();
        long endPoint = Constant.ONE_DAY_MILLISECONDS * fundBars[1].getDate().toEpochDay();

        Map map = new HashMap(3);
        map.put("maxBackdrawStartPoint", startPoint);
        map.put("maxBackdrawEndPoint", endPoint);
        String maxDrawback = Utils.calcRangeLabel(fundBars[0].getValue(), fundBars[1].getValue());
        map.put("maxBackdraw", maxDrawback);
        String conent = JSON.toJSONString(map);
        return conent;
    }

    @PostMapping("/fund/report/position/collection")
    @CrossOrigin
    public String generateFundReport(@RequestBody String codes) {
        List<String> codeList = getCodeList(codes);

        Map<String, PositionStatus> map = calculateStockPosition(codeList);
        List<PositionStatus> numberslist = sortMapByValues(map);

        Map result = new HashMap();
        result.put("info", numberslist);
        String conent = JSON.toJSONString(result);
        return conent;
    }

    private Map<String, PositionStatus> calculateStockPosition(List<String> codeList) {
        Map<String, PositionStatus> map = new HashMap<>();
        for (String code : codeList) {
            try {
                String content = favoriteStockService.getFundProfile(code);
                if (content == null) {
                    continue;
                }
                if (!content.contains(new_report_date)) {
                    continue;
                }
                FundFinancialReport fund = JSON.parseObject(content, FundFinancialReport.class);

                if (fund.getStockList() == null) {
                    continue;
                }

                double assetValue = fund.getAssetValue();

                List<StockList> stockList = fund.getStockList();

                for (StockList stock : stockList) {
                    String key = stock.getCode();
                    if (map.containsKey(key)) {
                        PositionStatus status = map.get(key);
                        status.number = status.number + 1;
                        status.value = status.value + stock.getPercent() * assetValue / 100;
                    } else {
                        PositionStatus status = new PositionStatus();
                        status.code = stock.getCode();
                        status.name = stock.getName();
                        status.number = 1;
                        status.value = stock.getPercent() * assetValue / 100;
                        map.put(key, status);
                    }
                }
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
        return map;
    }

    private List<String> getCodeList(String codes) {
        List<String> list = new ArrayList<>();
        codes = codes.replaceAll("=", "");
        codes = codes.replaceAll("%0A", ",");
        String[] lines = codes.split(",");
        for (String line : lines) {
            list.add(line);
        }
        return list;
    }


    @GetMapping("/fund/report/position/all")
    @CrossOrigin
    public String generateFundPositionStatusReport() {
        List<Stock> funds = favoriteStockService.getSymbols("FUNDS").getSymbols();
        removeDulplicatedFunds(funds);

        List<String> codeList = funds.stream().map(fund -> fund.getCode()).collect(Collectors.toList());

        Map<String, PositionStatus> map = calculateStockPosition(codeList);
        List<PositionStatus> list = sortMapByValues(map);

        List<PositionStatus> listResult = list.subList(0, 199);

        Map result = new HashMap();

        result.put("info", list);
//        result.put("values", valueslist);
        result.put("generateTime", LocalDate.now().toString());
        String content = JSON.toJSONString(result);
        reportService.storeStatistics("fundPosition-" + new_report_date, content);

        String top200CsvFormat = top200CsvFormat(list);
        reportService.storeStatistics("top200-fundPosition-" + new_report_date, top200CsvFormat);
        return content;
    }

    private String top200CsvFormat(List<PositionStatus> numberslist) {
        String newLine = System.getProperty("line.separator");
        StringBuilder sb = new StringBuilder();

        int index = Math.min(200, numberslist.size());

        for (int i = 0; i < index; i++) {
            PositionStatus positionStatus = numberslist.get(i);
            sb.append(positionStatus.getCode()).append(",").append(positionStatus.getName()).append(newLine);
        }
        return sb.toString();
    }

//    @GetMapping("/top200FundPosition")
//    public String top200FundPosition() {
//        String report = reportMapDB.get("top200-fundPosition-" + new_report_date);
//        return report;
//    }

//    @GetMapping("/fundPositionReport")
//    @CrossOrigin
//    public String funds() {
//        String fundReport = reportMapDB.get("fund-compare-report-" + new_report_date);
//        if (fundReport != null) {
//            return fundReport;
//        }
//
//        String reportOld = reportMapDB.get("fundPosition-" + previous_report_date);
//        String reportNew = reportMapDB.get("fundPosition-" + new_report_date);
//        JSONObject oldReport = JSON.parseObject(reportOld);
//        JSONObject newReport = JSON.parseObject(reportNew);
//
//        JSONArray newDataList = newReport.getJSONArray("info");
//        JSONArray oldDataList = oldReport.getJSONArray("info");
//
//
//        Map<String, JSONObject> oldFundMap = new HashMap<>();
//        {
//            for (int i = 0; i < oldDataList.size(); i++) {
//                JSONObject item = oldDataList.getJSONObject(i);
//                oldFundMap.put(item.getString("code"), item);
//            }
//        }
//
//        int limit = newDataList.size();
//        for (int i = 0; i < limit; i++) {
//            JSONObject item = newDataList.getJSONObject(i);
//            JSONObject oldItem = oldFundMap.get(item.getString("code"));
//            if (oldItem == null) {
//                //先不管了
//                BigDecimal value = Utils.convertYiYuan(item.getBigDecimal("value"));
//                item.put("value", value);
//                item.put("oldNumber", 0);
//                item.put("oldValue", 0);
//                item.put("numberChange", 0);
//                item.put("valueChange", 0);
//            } else {
//                int oldNumber = oldItem.getInteger("number");
//                int number = item.getInteger("number");
//                item.put("oldNumber", oldNumber);
//                String numberChange = "99999%";
//                if (oldNumber != 0) {
//                    numberChange = Utils.double2Percentage(Utils.calcRange(oldNumber, number));
//                }
//                item.put("numberChange", numberChange);
//
//
//                BigDecimal oldValue = Utils.convertYiYuan(oldItem.getBigDecimal("value"));
//                item.put("oldValue", oldValue);
//                BigDecimal value = Utils.convertYiYuan(item.getBigDecimal("value"));
//                item.put("value", value);
//
//                String valueChange = "99999%";
//                if (oldValue.doubleValue() > 0) {
//                    try {
//                        valueChange = Utils.double2Percentage(Utils.calcRange(oldValue, value));
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                    }
//
//                }
//                item.put("valueChange", valueChange);
//            }
//        }
//        String conent = JSON.toJSONString(newReport);
//        return conent;
//    }
//
//
//    @GetMapping("/fundPositionTrend")
//    public String fundPositionTrend() {
//
//        String report0 = reportMapDB.get("fundPosition-" + first_pre_previous_report_date);
//        String report1 = reportMapDB.get("fundPosition-" + pre_previous_report_date);
//        String report2 = reportMapDB.get("fundPosition-" + previous_report_date);
//        String report3 = reportMapDB.get("fundPosition-" + new_report_date);
//
//        JSONArray array0 = JSON.parseObject(report0).getJSONArray("info");
//        JSONArray array1 = JSON.parseObject(report1).getJSONArray("info");
//        JSONArray array2 = JSON.parseObject(report2).getJSONArray("info");
//        JSONArray array3 = JSON.parseObject(report3).getJSONArray("info");
//
//        Map<String, List<JSONObject>> map = new HashMap<>();
//
//        mergeItem(array0, map, first_pre_previous_report_date);
//        mergeItem(array1, map, pre_previous_report_date);
//        mergeItem(array2, map, previous_report_date);
//        mergeItem(array3, map, new_report_date);
//
//        List<List<JSONObject>> result = map.values().stream().filter(list -> {
//            if (list.size() < 3) {
//                return false;
//            }
//            try {
//                for (int i = 1; i < list.size(); i++) {
//                    if (list.get(i).getBigDecimal("value").compareTo(list.get(i - 1).getBigDecimal("value")) == -1) {
//                        return false;
//                    }
//                    String valueChange = Utils.double2Percentage(Utils.calcRange(list.get(i - 1).getBigDecimal("value"), list.get(i).getBigDecimal("value")));
//                    list.get(i).put("change", valueChange);
//                }
//            } catch (Exception e) {
//                logger.error(e.toString(), e);
//                return false;
//            }
//            return true;
//        }).collect(Collectors.toList());
//        toHTML(result);
//        String conent = JSON.toJSONString(result);
//        return conent;
//    }

    void toHTML(List<List<JSONObject>> result) {
        String s = html(
                head(
                        title("Title"),
                        meta().withContent("text/html").withCharset("utf-8"),
                        link().withRel("stylesheet").withHref("https://cdn.bootcdn.net/ajax/libs/bulma/0.9.3/css/bulma.min.css")
                ),
                body(
                        div(attrs(".container"),
                                h1(attrs(".title is-1"), "持续增仓的股票"),

                                each(result, list ->
                                        section(attrs(".section"),
                                                div(attrs(".container"),
                                                        h2(attrs(".title"), list.get(0).getString("name") + " - " + list.get(0).getString("code")),
                                                        table(attrs(".table is-bordered is-striped is-narrow is-hoverable is-fullwidth"),
                                                                thead(tr(th("日期"), th("数量"), th("持仓量"), th("增长"))),
                                                                tbody(
                                                                        each(list, i -> tr(
                                                                                td(i.getString("label")), td(i.getString("number")), td(i.getString("value")), td(i.getString("change"))
                                                                        ))
                                                                )
                                                        )
                                                )
                                        )
                                )
                        )
                )
        ).renderFormatted();
        System.out.println(s);
        File parent = new File("fund-trend.html");
        try {
            Files.asCharSink(parent, Charset.forName("UTF-8")).write(s);
        } catch (IOException e) {
            logger.error(e.toString(), e);
        }
    }

    private void mergeItem(JSONArray array1, Map<String, List<JSONObject>> map, String label) {
        for (int i = 0; i < array1.size(); i++) {
            JSONObject item = array1.getJSONObject(i);
            List<JSONObject> list = map.get(item.getString("code"));
            if (list == null) {
                list = new ArrayList<>();
                map.put(item.getString("code"), list);
            }
            item.put("label", label);
            item.put("change", "");
            list.add(item);
        }
    }

    //C类的和A 类的是同样的仓位
    private void removeDulplicatedFunds(List<Stock> funds) {
        Set<String> set = new HashSet<>();
        for (Stock fund : funds) {
            if (fund.getName().endsWith("A")) {
                String name = fund.getName().substring(0, fund.getName().length() - 1);
                set.add(name);
            }
        }

        Iterator<Stock> iterator = funds.iterator();
        while (iterator.hasNext()) {
            Stock stock = iterator.next();
            if (stock.getName().endsWith("C")) {
                String name = stock.getName().substring(0, stock.getName().length() - 1);
                if (set.contains(name)) {
                    iterator.remove();
                }
            }
        }
    }

    public List<PositionStatus> sortMapByValues(Map<String, PositionStatus> aMap) {
        List<PositionStatus> collect = aMap.entrySet()
                .stream()
                .sorted((p1, p2) -> (int) (p2.getValue().value - p1.getValue().value)).map(entry -> {
                    double value = entry.getValue().getValue();
                    entry.getValue().setValue((long) value);
                    return entry.getValue();
                })
                .collect(Collectors.toList());
        return collect;
    }

    public List<PositionStatus> sortMapByNumbers(Map<String, PositionStatus> aMap) {
        List<PositionStatus> collect = aMap.entrySet()
                .stream()
                .sorted((p1, p2) -> p2.getValue().number - p1.getValue().number).map(entry -> {
                    double value = entry.getValue().getValue();
                    entry.getValue().setValue((long) value);
                    return entry.getValue();
                })
                .collect(Collectors.toList());
        return collect;
    }


//    public String allFunds() throws IOException {
//        String content = reportMapDB.get("fundProfile");
//        return content;
//    }


    public String calculateFundsProfile() throws IOException {
        List<FundProfileInfo> resultList = new ArrayList<>();
        List<Stock> fundsSymbols = favoriteStockService.getSymbols("funds").getSymbols();
        for (Stock stock : fundsSymbols) {
            String content = favoriteStockService.getFundDetail(stock.getCode());
            if (content == null) {
                continue;
            }
            FundInfo fundDetail = JSON.parseObject(content, FundInfo.class);
            FundProfileInfo profileInfo = convert(fundDetail);
            content = favoriteStockService.getFundProfile(stock.getCode());
            FundFinancialReport financialReport = JSON.parseObject(content, FundFinancialReport.class);
            convert(profileInfo, financialReport);
            resultList.add(profileInfo);
        }
        String result = JSON.toJSONString(resultList);
//        reportMapDB.put("fundProfile", result);
        return result;
    }


    public List<FundInfo> getFundsProfiles() {
        List values = redisTemplate.opsForHash().values("funds-profiles");
        List<FundInfo> list = (List<FundInfo>) values.stream().map(item -> {
            String json = (String) item;
            FundInfo fund = JSON.parseObject(json, FundInfo.class);
            return fund;
        }).collect(Collectors.toList());
        return list;
    }

    public void generateFundsProfiles() {
        List<Stock> fundsSymbols = favoriteStockService.getSymbols("funds").getSymbols();
        for (Stock stock : fundsSymbols) {
            String content = favoriteStockService.getFundDetail(stock.getCode());
            if (content == null) {
                continue;
            }
            FundInfo fund = JSON.parseObject(content, FundInfo.class);
            fund.setFundBarList(null);
            content = JSON.toJSONString(fund);
            redisTemplate.opsForHash().put("funds-profiles", stock.getCode(), content);
        }
    }

    private void convert(FundProfileInfo profileInfo, FundFinancialReport financialReport) {
        if (financialReport == null) {
            return;
        }
        profileInfo.setMorningRate(financialReport.getMorningStar());
        profileInfo.setStockPercent(Utils.get2Float(financialReport.getStockPercent()));
    }


    private FundProfileInfo convert(FundInfo fundDetail) {
        FundProfileInfo profileInfo = new FundProfileInfo();

//        profileInfo.setStockPercent();
        profileInfo.setTotalShareNumber(fundDetail.getScale());
        profileInfo.setTotalShare(fundDetail.getScale() + "亿");
        profileInfo.setCode(fundDetail.getCode());
        profileInfo.setName(fundDetail.getName());
        profileInfo.setFoundDate(fundDetail.getFoundDate());
        profileInfo.setCurrentDate(fundDetail.getUpdateDate());
        profileInfo.setNetValue(fundDetail.getNetValue());
        profileInfo.setType(fundDetail.getType());
//        profileInfo.setMorningRate();
        profileInfo.setManagers(fundDetail.getManagers());
        profileInfo.setGrToday(fundDetail.getGrToday());
        profileInfo.setGrl1Week(fundDetail.getGrl1Week());
        profileInfo.setGrl1Month(fundDetail.getGrl1Month());
        profileInfo.setGrl3Month(fundDetail.getGrl3Month());
        profileInfo.setGrl6Month(fundDetail.getGrl6Month());
        profileInfo.setGrl1Year(fundDetail.getGrl1Year());
        profileInfo.setGrl2Year(getGrByPastTime(fundDetail.getFundBarList(), 2));
        profileInfo.setGrl3Year(getGrByPastTime(fundDetail.getFundBarList(), 3));
        profileInfo.setGrl5Year(getGrByPastTime(fundDetail.getFundBarList(), 5));
//        profileInfo.setGrThisYear();
        profileInfo.setGrBase(getGrBase(fundDetail.getFundBarList()));
        profileInfo.setCagr(getCagr(fundDetail.getFundBarList()));
        return profileInfo;
    }

    private float getCagr(List<FundBar> list) {
        FundBar start = list.get(0);
        FundBar end = list.get(list.size() - 1);

        double result = Utils.getCAGR(start.getDate(), end.getDate(), start.getValue(), end.getValue());
        return Utils.get2Float(result);
    }

    private float getGrThisYear(List<FundBar> list) {
        FundBar start = list.get(0);
        FundBar end = list.get(list.size() - 1);

        double result = (end.getValue() / start.getValue() - 1) * 100;
        return Utils.get2Float(result);
    }

    private float getGrBase(List<FundBar> list) {
        FundBar start = list.get(0);
        FundBar end = list.get(list.size() - 1);

        try {
            double result = (end.getValue() / start.getValue() - 1) * 100;
            return Utils.get2Float(result);
        } catch (Exception e) {
            logger.error("math error", e);
            return 0;

            //004666
        }


    }


    private float getGrByPastTime(List<FundBar> list, double year) {
        int days = (int) year * ONE_YEAR_DAYS;
        if (list.size() >= days + 1) {
            FundBar start = list.get(list.size() - days - 1);
            FundBar end = list.get(list.size() - 1);

            try {
                double result = (end.getValue() / start.getValue() - 1) * 100;
                return Utils.get2Float(result);

            } catch (Exception e) {
                logger.error("math error", e);
                return 0;
            }
        } else {
            return 0;
        }
    }

    public void genDemarkReport() throws IOException {
        String content = demarkService.fundDemark("funds", 42);
        reportService.storeAnalysis("Fund-Demark-daily", content);
    }

    public String getDemarkReport() {
        return reportService.getAnalysis("Fund-Demark-daily");
    }

}
